Rustで暗号利用モードをいろいろ実装してみた
こんにちは、リテールアプリ共創部のmorimorikochanです。
情報処理安全確保支援士の試験が直前まで迫ってきました...追い込みに力が入りますね🔥
それに関連して以前S-DESをRustで実装しましたが、その際に暗号利用モードとしてECBを選択し実装しました。
ECBモードはしっかり理解できたものの、CBC、CFB、OFB、CTRモードがまだまだ頭に入らなかったので、今回は文章・図にまとめてRustで実装してみることにしました。
初期化ベクトル(IV)について
まずは、これらの暗号化モードで重要な初期化ベクトル(IV)について見ていきましょう。
IVは主に最初のブロックへのinputとして使用されます。ブロック長と同じサイズである場合が多いです。
この値は特に秘密にする必要はありませんが、予測不可能な値にする必要があります(予測可能になってしまうと、同じ平文を暗号化すると同じ暗号文が出てきてしまいリスクになるため)。
復号側は暗号化した際と同じIVを利用しなければならないため、暗号側はなんらかの方法でIVを共有する必要があります。
今回の実装では、IVを暗号文の先頭に付与し、復号側は先頭ブロックをIVとして利用するような実装にしてみました。
ちなみに、WEPという古い無線LANのセキュリティプロトコルではこのIVのサイズが小さく(24bit)、同じIVが頻繁に使いまわされるために再利用攻撃に対して脆弱だったようです
暗号利用モード
まず各暗号利用モードについてまとめてみました。
ECB (Electronic Codebook)
- 前回紹介した通り、特に変わった処理は行いません
- 暗号化・復号どちらも並列に処理できるのが特徴です
CBC (Cipher Block Chaining)
- 暗号文の生成手順は大きく2ステップに分かれます
- 1.前のブロックの暗号文と平文をXOR
- 2.その結果を暗号化
- 暗号化は並列処理できませんが、復号は並列処理できます
- 全てのブロックで並列に暗号文の復号が可能です
CFB (Cipher Feedback)
- 暗号文の生成手順は大きく2ステップに分かれます
- 1.前のブロックの暗号文を暗号化
- 2.その結果と平文をXOR
- CBCと同じく暗号化は並列処理できませんが、復号は並列化可能です
- 復号時には復号処理を使用せず暗号化処理を使用します
- 復号時にはアルゴリズム(今回でいう
S-DES
)の復号処理は利用せず暗号化処理を利用します、びっくりですよね - どうやって平文を手に入れるか考えると自明ではありますが不思議です
- 復号時にはアルゴリズム(今回でいう
OFB (Output Feedback)
- IVを繰り返し暗号化し、鍵ストリームを生成します
- 各ブロックの暗号文は鍵ストリームを使い以下のように算出されます
- 1ブロック目:平文とIVを1回暗号化したものをXOR
- 2ブロック目:平文とIVを2回暗号化したものをXOR
- ...
- Nブロック目:平文とIVをN回暗号化したものをXOR
- 暗号化・復号どちらも並列に処理できません
- 復号時には復号処理を使用せず暗号化処理を使用します
CTR (Counter)
- OFBと同様に鍵ストリームを使用しますが、暗号化せずに単純にインクリメントします
- 最初の値をIVとは呼ばずに
nonce
と呼ぶみたいです
- 最初の値をIVとは呼ばずに
- 各ブロックの暗号文は鍵ストリームを使い以下のように算出されます
- 1ブロック目:(平文)と(IVを1回インクリメントして暗号化したもの)をXOR
- 2ブロック目:(平文)と(IVを2回インクリメントして暗号化したもの)をXOR
- ...
- Nブロック目:(平文)と(IVをN回インクリメントして暗号化したもの)をXOR
- 暗号化と復号の両方が並列処理可能です
- 復号時には復号処理を使用せず暗号化処理を使用します
Rustで実装してみた
各暗号利用モードで実装してみました。
それぞれ以下の処理を行っています。
- テキストファイル(
plain.txt
)を暗号化 - 暗号文を
*.encrypted.txt
として保存 *.encrypted.txt
を読み取り復号- 復号した内容を
*.decrypted.txt
として保存
実装する上では、各ブロックの処理がどこからどこまでの範囲かを意識するとすんなり実装することができましたので参考にしてください(上記の図で縦線で表現されている部分です)。
ECB
CBC
CFB
OFB
CTR
CTRモードはRustで書くと各ブロックの処理の独立性が他の暗号利用モードと比べて高かったので、とても書きやすかったです。Rayonを使って並列に処理することも簡単にできるはずだと思います。
まとめ
これらのモードをRustで実装してみると、各モードの特徴がより明確になりました。特に、並列処理の可否や復号時の処理の違いが興味深いですね。
セキュリティ関連の実装は普段の業務とは全く別の難しさがありますが、こうやって少しずつ理解を深めていくのが大切だなーと感じています。
みなさんも、暗号化について興味があれば、ぜひ実装してみてください!